import PLCSymbolAndScope.IDGenerator;
import PLCSymbolAndScope.PLCScopeStack;
import PLCSymbolAndScope.PLCSymbolTables.PLCTotalSymbolTable;
import PLCSymbolAndScope.PLCSymbols.*;
import antlr4.PLCSTPARSERBaseVisitor;
import antlr4.PLCSTPARSERParser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import static PLCSymbolAndScope.PLCScopeStack.*;
import static PLCSymbolAndScope.PLCSymbolTables.PLCTotalSymbolTable.*;

public class PLCVisitor extends PLCSTPARSERBaseVisitor<List<PLCSymbol>> {

    public PLCVisitor(){
        super();
        PLCScopeStack.stackInit();
        PLCTotalSymbolTable.totalTableInit();
    }

    @Override
    public List<PLCSymbol> visitClass_decl(PLCSTPARSERParser.Class_declContext ctx) {

        //返回对象

        //创建classSymbol
        PLCClassDeclSymbol classSymbol = new PLCClassDeclSymbol(ctx.class_type_name().getText());

        //入栈
        PLCScopeStack.push(classSymbol);

        //获取class是否时final或abstract
        if(ctx.FINALORABSTRACT() != null){
            if(ctx.FINALORABSTRACT().getText() == "FINAL"){
                classSymbol.classModifier = PLCModifierEnum.ClassModifier.FINAL;
            }else {
                classSymbol.classModifier = PLCModifierEnum.ClassModifier.ABSTRACT;
            }
        }

        //调用visit访问使用的命名空间，将命名空间加入相应符号表中
        if(ctx.using_directive() != null) {
            visit(ctx.using_directive());
        }

        //获取继承的基类符号
        if(ctx.interface_name_list() != null) {
            visit(ctx.interface_name_list());
            //然后添加到classSymbol中
        }

        //获取变量段声明符号
        if(ctx.func_var_decls().size() != 0) {
            //使用func_var_decls的迭代器
            Iterator<PLCSTPARSERParser.Func_var_declsContext> funcVarDeclsContextIterator = ctx.func_var_decls().listIterator();
            //逐一访问每个变量段
            while(funcVarDeclsContextIterator.hasNext()){
                visit(funcVarDeclsContextIterator.next());
            }
        }

        //获取特殊变量段声明
        if(ctx.other_var_decls().size() != 0){
            //使用迭代器
            Iterator<PLCSTPARSERParser.Other_var_declsContext> otherVarDeclsContextIterator = ctx.other_var_decls().listIterator();
            //逐一访问每个变量段
            while(otherVarDeclsContextIterator.hasNext()){
                visit(otherVarDeclsContextIterator.next());
            }
        }

        //访问method结点，添加method符号
        if(ctx.method_decl().size() != 0){

            visit(ctx.method_decl(0));

        }

        //TODO:检查是否实现了继承接口的所有方法
        //TODO:检查重定义的方法是否是重写
        //将class作用域出栈
        PLCScopeStack.pop();

        return null;

    }

    @Override public List<PLCSymbol> visitUsing_directive(PLCSTPARSERParser.Using_directiveContext ctx) {

        //TODO:静态语言检查是否存在该命名空间后添加到class的usingNS
        Iterator<PLCSTPARSERParser.Namespace_h_nameContext> namespacehNameListIterator = ctx.namespace_h_name().listIterator();

        while(namespacehNameListIterator.hasNext()){
            //访问每句
            visit(namespacehNameListIterator.next());

        }


        return null;

    }

    //获取使用的命名空间
    @Override
    public List<PLCSymbol> visitNamespace_h_name(PLCSTPARSERParser.Namespace_h_nameContext ctx) {

        //TODO:静态检查
        Iterator<PLCSTPARSERParser.Namespace_nameContext> namespacehNameIterator = ctx.namespace_name().listIterator();
        PLCSymbol namespaceSymbol = usingNamespaceSemanticCheck(namespacehNameIterator);

        //TODO:静态检查，递归寻找上层作用域中定义的命名空间,将找到的命名空间符号返回，
        //添加当前作用域使用的作用域符号
        if(namespaceSymbol != null) {
            PLCScopeStack.currentScope.addUsingNamespace((PLCNamespaceDeclSymbol) namespaceSymbol);
        }

        return null;

    }

    //获取继承的基类
    @Override
    public List<PLCSymbol> visitClass_type_access(PLCSTPARSERParser.Class_type_accessContext ctx) {
        //添加基类
        PLCSymbol baseClassSymbol = null;
        //检查是否继承某一命名空间下的class
        if(ctx.namespace_name().size() != 0) {
            Iterator<PLCSTPARSERParser.Namespace_nameContext> namespaceNameListIterator = ctx.namespace_name().listIterator();

            baseClassSymbol = usingNamespaceSemanticCheck(namespaceNameListIterator)
                            .localSymbolTable.findSymbol(ctx.class_type_name().getText());
        }else{
            //TODO:重载这个find
            baseClassSymbol = currentScope.findSymbol(ctx.class_type_name().getText());

        }

        if(baseClassSymbol == null){

            System.out.println("Extends class is null!");

        }else{
            if(currentScope.getDeclSymbol() instanceof PLCClassDeclSymbol){
                ((PLCClassDeclSymbol)currentScope.getDeclSymbol()).setBaseClass((PLCClassDeclSymbol) baseClassSymbol);
            }
        }

        return null;

    }

    //获取实现的接口(类似检查获取基类符号)
    @Override
    public List<PLCSymbol> visitInterface_name_list(PLCSTPARSERParser.Interface_name_listContext ctx) {
        return visitChildren(ctx);
    }

    @Override
    public List<PLCSymbol> visitFunc_var_decls(PLCSTPARSERParser.Func_var_declsContext ctx) {
        //返回下层中符号的类型信息
        return visit(ctx.var_decls());

    }

    //var变量段
    @Override
    public List<PLCSymbol> visitVar_decls(PLCSTPARSERParser.Var_declsContext ctx) {
        //创建临时空符号
        PLCVariable tempVariable = new PLCVariable(1);

        boolean conInfo = false;

        if(Objects.equals(ctx.getChild(1).getText(), "CONSTANT")){
            conInfo = true;
        }

        //获取变量段声明成员信息
        if(ctx.Access_Spec() != null){
            if(Objects.equals(ctx.Access_Spec().getText(), "PRIVATE")){

                tempVariable.accessModifier = PLCModifierEnum.AccessModifier.PRIVATE;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "PUBLIC")){

                tempVariable.accessModifier = PLCModifierEnum.AccessModifier.PUBLIC;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "INTERNAL")){

                tempVariable.accessModifier = PLCModifierEnum.AccessModifier.INTERNAL;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "PROTECTED")){

                tempVariable.accessModifier = PLCModifierEnum.AccessModifier.PROTECTED;

            }
        }

        //访问变量段组装变量信息然后将变量加入符号表
        if(ctx.var_decl_init() != null) {

            //使用迭代器
            Iterator<PLCSTPARSERParser.Var_decl_initContext> varDeclInitContextIterator = ctx.var_decl_init().listIterator();

            while(varDeclInitContextIterator.hasNext()){
                //调用visit函数访问每一个var语句
                List<PLCSymbol> varDeclInitList = visit(varDeclInitContextIterator.next());
                //给每个list中的符号组装变量段信息
                Iterator<PLCSymbol> varDeclInitItetator = varDeclInitList.listIterator();//使用迭代器来组装信息
                while(varDeclInitItetator.hasNext()){

                    //对类型进行cast
                    PLCVariable plcVariable = ((PLCVariable) varDeclInitItetator.next());
                    //设置constant信息
                    plcVariable.ifConst = conInfo;
                    //设置变量段访问信息
                    plcVariable.accessModifier = tempVariable.accessModifier;
                    //将组装好的符号添加到总表中
                    addSymbol(plcVariable);
                    //将组装好的符号添加到当前作用域中符号表
                    currentScope.getScopeSymbolTable().addSymbol(plcVariable);

                    System.out.println("符号"+plcVariable.name+"完成添加");
                }
            }
        }

        return null;

    }

    //var变量段中变量初始化
    @Override
    public List<PLCSymbol> visitVardeclinit(PLCSTPARSERParser.VardeclinitContext ctx) {
        //该条语句声明的符号数组(将下层语法的访问器factor函数传上来的符号名称和符号类型组装后存储在该list中然后向上返回)
        List<PLCSymbol> varDeclInitList = new ArrayList<>();

        //获取声明的变量个数以及名称信息
        List<PLCSymbol> variableList = visit(ctx.variable_list());
        //创建variableList的迭代器
        Iterator<PLCSymbol> variableIterator = variableList.listIterator();
        //获取simple_spec_init中的类型信息
        if(ctx.simple_spec_init() != null){

            //获取符号类型信息
            List<PLCSymbol> simpleSpecInitList = visit(ctx.simple_spec_init());

            //使用迭代器把符号的名称和类型信息进行组装
            while(variableIterator.hasNext()){

                //组装符号类型
                PLCSymbol plcSymbol = variableIterator.next();

                PLCSymbol symbol = simpleSpecInitList.get(0);
                symbol.setSymbolId(plcSymbol.getSymbolId());
                symbol.setName(plcSymbol.getName());
                //加入返回list中
                varDeclInitList.add(symbol);
            }

        }else if(ctx.ref_spec_init() != null){
            List<PLCSymbol> refSpecInitList = visit(ctx.ref_spec_init());

            while(variableIterator.hasNext()){

                PLCSymbol plcSymbol = variableIterator.next();

                PLCSymbol symbol = refSpecInitList.get(0);
                symbol.setSymbolId(plcSymbol.getSymbolId());
                symbol.setName(plcSymbol.getName());
                //加入返回list中
                varDeclInitList.add(symbol);

            }

        }else if(ctx.array_var_decl_init() != null){

            //访问数组类型
            List<PLCSymbol> arrayVarSpecInitList = visit(ctx.array_var_decl_init());
            while(variableIterator.hasNext()){

                PLCSymbol plcSymbol = variableIterator.next();

                PLCSymbol symbol = arrayVarSpecInitList.get(0);
                symbol.setSymbolId(plcSymbol.getSymbolId());
                symbol.setName(plcSymbol.getName());
                //加入返回list中
                varDeclInitList.add(symbol);

            }
        }

        //返回
        return varDeclInitList;

    }

    //获取变量段中定义的符号名称
    @Override
    public List<PLCSymbol> visitVariable_list(PLCSTPARSERParser.Variable_listContext ctx) {

        //返回该行定义的一个或多个符号变量的名称信息
        List<PLCSymbol> variableList = new ArrayList<>();

        //如果存在多个声明变量，则创建多个符号进行存储
        Iterator<PLCSTPARSERParser.Variable_nameContext> variableNameListIterator = ctx.variable_name().listIterator();

        return variableList;
    }

    //获取变量段中定义的符号类型
    @Override
    public List<PLCSymbol> visitSimple_spec_init(PLCSTPARSERParser.Simple_spec_initContext ctx) {
        //factor函数获取定义变量的数据类型
        //获取符号类型
        List<PLCSymbol> simpleSpecList = visitSimple_spec(ctx.simple_spec());

        //检查符号是否初始化
        if(ctx.constant_expr() != null){

            System.out.println(ctx.simple_spec().getText());
            System.out.println("Symbol has initiated");

        }

        return simpleSpecList;

    }

    //获取符号类型（获取基本类型）
    @Override
    public List<PLCSymbol> visitSimple_spec(PLCSTPARSERParser.Simple_specContext ctx) {
        //factor函数，返回符号类型

        if(ctx.elem_type_name() != null){
            //访问elemTypeName获取符号类型信息
            return visit(ctx.elem_type_name());

        }else{
            //获取其他命名空间下的基本类型 TODO:存疑
            return visit(ctx.simple_type_access());

        }


    }

    //获取符号类型（获取基本类型）
    @Override
    public List<PLCSymbol> visitElem_type_name(PLCSTPARSERParser.Elem_type_nameContext ctx) {

        //获取时间类型名返回给上层visitor进行组装
        if (ctx.Date_Type_Name()!=null || ctx.Time_Type_Name()!=null){
            //创建空名称符号只设置类型，不设置名称以及id
            List<PLCSymbol> dataTypeList = new ArrayList<>();
            //创建variable类型符号
            PLCSymbol tempSymbol = new PLCVariable(1);
            //返回符号信息
            if(ctx.Date_Type_Name()!=null){
                tempSymbol.setTypeId(findTypeByName(ctx.Date_Type_Name().getText()));
            }else{
                tempSymbol.setTypeId(findTypeByName(ctx.Time_Type_Name().getText()));
            }
            //加入列表返回
            dataTypeList.add(tempSymbol);

            return dataTypeList;

        }else if(ctx.numeric_type_name() != null){
            //获取整型浮点数类型
            List<PLCSymbol> numericTypeNameList = new ArrayList<>();
            //创建variable符号向上返回信息
            PLCSymbol tempSymbol = new PLCVariable(1);
            tempSymbol.setTypeId(findTypeByName(ctx.numeric_type_name().getText()));
            //将符号信息插入数组以待返回
            numericTypeNameList.add(tempSymbol);
            return numericTypeNameList;

        }else if(ctx.bit_str_type_name() != null){ //获取布尔类型以及位串类型

            //
            List<PLCSymbol> bitTypeNameList = new ArrayList<>();

            //创建临时符号存储信息
            PLCSymbol tempsymbol = new PLCVariable(1);
            tempsymbol.setTypeId(findTypeByName(ctx.bit_str_type_name().getText()));

            bitTypeNameList.add(tempsymbol);
            return bitTypeNameList;

        }else{  //获取字符串类型
            //
            List<PLCSymbol> stringTypeNameList = visit(ctx.string_type_name());
            //字符串类型
            PLCStringDeclSymbol plcStringDeclSymbol = new PLCStringDeclSymbol();
            stringTypeNameList.add(plcStringDeclSymbol);

            if(Objects.equals(ctx.string_type_name().getChild(0).getText(), "STRING")
                    ||Objects.equals(ctx.string_type_name().getChild(0).getText(), "WSTRING")){
                //设置符号类型ID
                plcStringDeclSymbol.setTypeId(findTypeByName(ctx.string_type_name().getChild(0).getText()));
                //添加到返回list中

                if(ctx.string_type_name().Unsigned_int()!=null) {
                    //获取字符串长度定义
                    plcStringDeclSymbol.length = Integer.parseInt(ctx.string_type_name().Unsigned_int().getText());

                }else if(ctx.string_type_name().identifier() != null){
                    //TODO静态检查搜索identifier符号是否定义
                    plcStringDeclSymbol.plcVariableLength =
                            (PLCVariable)currentScope.findSymbol(ctx.string_type_name().identifier().getText());
                }

            }else{
                //CHAR、WCHAR类型
                plcStringDeclSymbol.setTypeId(findTypeByName(ctx.string_type_name().getText()));
            }

            return stringTypeNameList;
        }

    }

    //访问引用类型
    @Override
    public List<PLCSymbol> visitRef_spec_init(PLCSTPARSERParser.Ref_spec_initContext ctx) {
        //访问ref类型
        List<PLCSymbol> refSpecInitList = visit(ctx.ref_spec());
        //静态语义检查
        if(ctx.ref_value() != null){
            return visit(ctx.ref_value());
        }
        return refSpecInitList;
    }

    //访问引用类型
    @Override
    public List<PLCSymbol> visitRef_spec(PLCSTPARSERParser.Ref_specContext ctx) {
        return visit(ctx.data_type_access());
    }

    //访问类型
    @Override
    public List<PLCSymbol> visitData_type_access(PLCSTPARSERParser.Data_type_accessContext ctx) {
        List<PLCSymbol> dataTypeAccessList = new ArrayList<>();

        if(ctx.elem_type_name() != null){
            dataTypeAccessList = visit(ctx.elem_type_name());

        }else {
            dataTypeAccessList = visit(ctx.derived_type_access());

        }
        return dataTypeAccessList;
    }

    @Override
    public List<PLCSymbol> visitArray_var_decl_init(PLCSTPARSERParser.Array_var_decl_initContext ctx) {
        return visit(ctx.array_spec_init());
    }

    @Override
    public List<PLCSymbol> visitArray_spec_init(PLCSTPARSERParser.Array_spec_initContext ctx) {
        List<PLCSymbol> arraySpecInitList = visit(ctx.array_spec());

        visit(ctx.array_init());

        return arraySpecInitList;

    }

    @Override
    public List<PLCSymbol> visitArrayTypeAccess(PLCSTPARSERParser.ArrayTypeAccessContext ctx) {
        //
        return visit(ctx.array_type_access());
    }

    @Override
    public List<PLCSymbol> visitArray_type_access(PLCSTPARSERParser.Array_type_accessContext ctx) {
        List<PLCSymbol> arrayTypeAccessList = new ArrayList<>();
        //搜索命名空间
        if(ctx.namespace_name().size() != 0){
            Iterator<PLCSTPARSERParser.Namespace_nameContext> namespaceNameListIterator = ctx.namespace_name().listIterator();

            PLCNamespaceDeclSymbol plcNamespaceDeclSymbol= (PLCNamespaceDeclSymbol)
                    (usingNamespaceSemanticCheck(namespaceNameListIterator));
            //如果命名空间不为空，则在该命名空间下搜索使用的数组类型
            if(plcNamespaceDeclSymbol != null) {
                PLCArrayDeclSymbol plcArrayDeclSymbol = (PLCArrayDeclSymbol)
                        plcNamespaceDeclSymbol.importScope.findSymbol(ctx.array_type_name().getText());//(待更正)
                //找到相应的符号添加到list
                if(plcArrayDeclSymbol != null){
                    arrayTypeAccessList.add(plcArrayDeclSymbol);
                }else {
                    System.out.println("Can't not find this array symbol");
                }
            }
        }else {

            PLCArrayDeclSymbol plcArrayDeclSymbol
                    = (PLCArrayDeclSymbol) currentScope.findSymbol(ctx.array_type_name().getText());
            if(plcArrayDeclSymbol != null){
                arrayTypeAccessList.add(plcArrayDeclSymbol);
            }else{
                System.out.println("Can't find this array symbol");
            }

        }

        return arrayTypeAccessList;
    }

    @Override public List<PLCSymbol> visitArrayDirectSpec(PLCSTPARSERParser.ArrayDirectSpecContext ctx) {
        PLCArrayDeclSymbol plcArrayDeclSymbol = new PLCArrayDeclSymbol();
        int dimension = ctx.subrange().size();

        return visitChildren(ctx);
    }

    //external变量段
    @Override
    public List<PLCSymbol> visitExternal_var_decls(PLCSTPARSERParser.External_var_declsContext ctx) {
        return visitChildren(ctx);
    }

    //访问external变量段每一句变量声明
    @Override
    public List<PLCSymbol> visitExternal_decl(PLCSTPARSERParser.External_declContext ctx) {

        return visitChildren(ctx);

    }

    //其他变量段（retain、nonRetain变量段）
    @Override
    public List<PLCSymbol> visitOther_var_decls(PLCSTPARSERParser.Other_var_declsContext ctx) {
        return visitChildren(ctx);
    }

    //Retain变量段
    @Override
    public List<PLCSymbol> visitRetain_var_decls(PLCSTPARSERParser.Retain_var_declsContext ctx) {
        return visitChildren(ctx);
    }

    //nonRetain变量段
    @Override
    public List<PLCSymbol> visitNo_retain_var_decls(PLCSTPARSERParser.No_retain_var_declsContext ctx) {
        return visitChildren(ctx);
    }

    //直接地址变量段
    @Override
    public List<PLCSymbol> visitLoc_partly_var_decl(PLCSTPARSERParser.Loc_partly_var_declContext ctx) {
        return visitChildren(ctx);
    }

    //方法声明
    @Override
    public List<PLCSymbol> visitMethod_decl(PLCSTPARSERParser.Method_declContext ctx) {

        //创建method符号以待返回
        List<PLCSymbol> methodSymbolList = new ArrayList<>();

        PLCMethodDeclSymbol methodSymbol = new PLCMethodDeclSymbol();//待修改成调用symbolTableGenerator下的create符号方法

        methodSymbolList.add(methodSymbol);

        //获取类方法的访问方式


        if(ctx.Access_Spec() != null){
            if(Objects.equals(ctx.Access_Spec().getText(), "PRIVATE")){

                methodSymbol.accessModifier= PLCModifierEnum.AccessModifier.PRIVATE;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "PUBLIC")){

                methodSymbol.accessModifier= PLCModifierEnum.AccessModifier.PUBLIC;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "INTERNAL")){

                methodSymbol.accessModifier= PLCModifierEnum.AccessModifier.INTERNAL;

            }else if(Objects.equals(ctx.Access_Spec().getText(), "PROTECTED")){

                methodSymbol.accessModifier= PLCModifierEnum.AccessModifier.PROTECTED;

            }

        }

        //检测方法是否是final或Abstract
        if(ctx.FINALORABSTRACT()!=null){
            if(ctx.FINALORABSTRACT().getText() == "FINAL"){

                methodSymbol.methodModifier = PLCModifierEnum.ClassModifier.FINAL;

            }else{

                methodSymbol.methodModifier = PLCModifierEnum.ClassModifier.ABSTRACT;

            }
        }else{

            methodSymbol.methodModifier = PLCModifierEnum.ClassModifier.NONE;

        }

        //获取方法名称
        methodSymbol.name = ctx.method_name().getText();

        //设置符号ID
        methodSymbol.symbolId = IDGenerator.newSymbolId();

        //获取方法类型ID
        methodSymbol.typeId = visit(ctx.data_type_access()).get(0).typeId;

        //获取变量段声明变量
        if(ctx.func_var_decls().size() != 0 ) {
            int j = ctx.func_var_decls().size();
            for(int i =0; i <j;i++) {
                //获取func_var变量段声明的符号（在下层递归调用中已经添加定义的符号）
                List<PLCSymbol> funcVarDeclsList = visit(ctx.func_var_decls(i));
                //
            }
        }
        if(ctx.io_var_decls().size() != 0){
            int j = ctx.io_var_decls().size();
            for(int i =0; i <j;i++) {
                //获取func_var变量段声明的符号（在下层递归调用中已经添加定义的符号）
                List<PLCSymbol> funcVarDeclsList = visit(ctx.io_var_decls(i));
                //
            }
        }
        if(ctx.temp_var_decls().size() != 0){
            int j = ctx.temp_var_decls().size();
            for(int i =0; i <j;i++) {
                //获取func_var变量段声明的符号（在下层递归调用中已经添加定义的符号）
                List<PLCSymbol> funcVarDeclsList = visit(ctx.temp_var_decls(i));
                //
            }
        }

        //访问函数体信息
        List<PLCSymbol> funcBody = visit(ctx.func_body());

        return methodSymbolList;
    }

    //输入输出变量段
    @Override
    public List<PLCSymbol> visitIo_var_decls(PLCSTPARSERParser.Io_var_declsContext ctx) {

        if(ctx.input_decls() != null){

            visit(ctx.input_decls());

        }else if(ctx.output_decls() != null){

            visit(ctx.output_decls());

        }else if(ctx.in_out_decls() != null){

            visit(ctx.in_out_decls());

        }

        return null;

    }

    //访问输入变量
    @Override
    public List<PLCSymbol> visitInput_decls(PLCSTPARSERParser.Input_declsContext ctx) {

        //检查变量段是否有retain信息
        if(ctx.RETAINORNONRETAIN() != null){
            if(ctx.RETAINORNONRETAIN().getText() == "RETAIN"){

            }else{

            }

        }else{

        }

        if(ctx.input_decl().size() != 0){
            //使用迭代器
            Iterator<PLCSTPARSERParser.Input_declContext> inputDeclContextIterator = ctx.input_decl().listIterator();

            while(inputDeclContextIterator.hasNext()){

                //访问每一个inputdecl获取下层函数传回的符号
                List<PLCSymbol> inputDeclContextList = visit(inputDeclContextIterator.next());

                //然后设置所处变量段类型以及信息(待填)

            }

        }

        return null;

    }

    //临时变量段
    @Override
    public List<PLCSymbol> visitTemp_var_decls(PLCSTPARSERParser.Temp_var_declsContext ctx) {
        return visitChildren(ctx);
    }



}
